`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2023/12/10 21:48:27
// Design Name:
// Module Name: uart
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module uart
  #(
     parameter SYS_FRENCY =50_000_000 ,//系统频率50M
     parameter BAUD_FRENCY =115200//波特率
   )(
     input  clk           ,
     input  rst           ,
     input  [15:0]in      ,
     input  load          ,
     input  [15:0]address ,
     input  uart_rx_pin   ,
     output uart_tx_pin   ,
     output [15:0]out
   );

  localparam UART_STATUS    = 2'b00;
  localparam UART_TX_DATA   = 2'b01;
  localparam UART_RX_DATA   = 2'b10;

  // uart_ram[UART_STATUS]
  // ro. bit[0]: tx busy, 1 = busy, 0 = idle
  // rw. bit[1]: rx over, 1 = over, 0 = receiving
  logic [15:0] uart_ram[3:0];

  initial
  begin
    integer i;
    for (i = 0; i <= 'h4; i = i + 1)
    begin
      uart_ram[i] = 0;
    end
  end

  logic uart_send_en, uart_tx_done, uart_rx_done;
  logic [7:0] uart_rx_data;

  // uart Memory Write
  always @(posedge clk)
  begin
    if (rst == 0)
    begin
      uart_ram[UART_STATUS] <= 0;
      uart_ram[UART_TX_DATA] <= 0;
      uart_ram[UART_RX_DATA] <= 0;
      uart_send_en <= 0;
    end
    else
    begin
      if (load == 1)
      begin
        case (address[1:0])
          UART_STATUS:
          begin
            // 仅 rx status 允许 write
            uart_ram[UART_STATUS][1] <= in[1];
          end
          UART_TX_DATA:
          begin
            if (uart_ram[UART_STATUS][0] == 1'b0)
            begin
              uart_send_en <= 1'b1; // enable tx send
              uart_ram[UART_STATUS][0] <= 1'b1; // tx busy
              uart_ram[UART_TX_DATA] <= in; // tx data
            end
          end
        endcase
      end
      else
      begin
        uart_send_en <= 1'b0;
        if (uart_tx_done == 1'b1)
        begin
          uart_ram[UART_STATUS][0] <= 1'b0;
        end
        if (uart_rx_done)
        begin
          uart_ram[UART_STATUS][1] <= 1'b1;
          uart_ram[UART_RX_DATA] <= uart_rx_data;
        end
      end
    end
  end

  // *************************** TX发送 ****************************
  uart_tx
    #(.SYS_FRENCY(SYS_FRENCY),
      .BAUD_FRENCY(BAUD_FRENCY))
    uart_tx_impl(
      .clk(clk),
      .rst_n(rst),
      .send_en(uart_send_en),
      .tx_data(uart_ram[UART_TX_DATA][7:0]),
      .rs232_tx(uart_tx_pin),
      .tx_done(uart_tx_done)
    );

  // *************************** RX接收 ****************************
  uart_rx
    #(.SYS_FRENCY(SYS_FRENCY),
      .BAUD_FRENCY(BAUD_FRENCY))
    uart_rx_impl(
      .clk(clk),
      .rst_n(rst),
      .rs232_rx(uart_rx_pin),
      .rx_done(uart_rx_done),
      .rx_data(uart_rx_data)
    );

  // read
  assign out = uart_ram[address[1:0]];

endmodule

module uart_rx
  #(
     parameter SYS_FRENCY =50_000_000 ,//系统频率50M
     parameter BAUD_FRENCY =115200//波特率
   )

   (
     input           clk          ,
     input           rst_n        ,
     input           rs232_rx     ,
     output reg      rx_done      ,
     output reg [7:0]rx_data
   );
  localparam CNT_MAX = SYS_FRENCY/BAUD_FRENCY;
  reg        en_cnt          ; //计数器使能控制
  reg [15:0] cnt             ;
  wire       bps_vld         ;  //计数到每bit数据的中心产生一个高电平
  reg [3:0]  bps_cnt         ;  //接受位数的计数器
  reg [1:0]  rs232_rx_filter ;  //用两个寄存器消除亚稳态
  reg        rs232_rx_d;    //用一个寄存器来识别下降沿
  wire       nedge;                //下降沿信号
  reg [9:0]  rx_data_r;
  wire       rx_done_r;
  //消除亚稳态
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      rs232_rx_filter<=2'b11;
    else
      rs232_rx_filter<={rs232_rx_filter[0],rs232_rx};
  end
  //下降沿识别
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      rs232_rx_d<=1'b1;
    else
      rs232_rx_d<=rs232_rx_filter[1];
  end
  assign nedge=rs232_rx_d&(!rs232_rx_filter[1]);
  //en_cnt
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      en_cnt<=1'b0;
    else if(nedge)
      en_cnt<=1'b1;
    else if(rx_done_r)
      en_cnt<=1'b0;
    else
      en_cnt<=en_cnt;
  end
  //cnt
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cnt<='d0;
    else if(en_cnt)
    begin
      if(cnt==CNT_MAX)
        cnt<='d0;
      else
        cnt<=cnt+1'b1;
    end
    else
      cnt<='d0;
  end
  //在中间的时刻采集信号线上的数据，表明数据有效
  assign bps_vld=cnt==(CNT_MAX>>1);
  //bps_cnt  计数到每bit信号的中心然后再读取数值
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      bps_cnt<='d0;
    else if(en_cnt)
    begin
      if(cnt==CNT_MAX)
        bps_cnt<=bps_cnt+1'b1;
      else
        bps_cnt<=bps_cnt;
    end
    else
      bps_cnt<='d0;
  end
  //rx_data 读出数据
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      rx_data_r<='d0;
    else if (bps_vld)
    begin
      case (bps_cnt)
        0,1,2,3,4,5,6,7,8,9:
          rx_data_r<={rs232_rx_filter[1],rx_data_r[9:1]};
        default:
          rx_data_r<=rx_data_r;
      endcase
    end
    else
      rx_data_r<=rx_data_r;
  end
  //在停止位的的3/4处产生结束信号
  assign rx_done_r=(cnt==CNT_MAX-(CNT_MAX>>2))&&(bps_cnt==4'd9);
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      rx_data<='d0;
    else if(rx_done_r)
    begin
      if(!rx_data_r[0]&rx_data_r[9])
        rx_data<=rx_data_r[8:1];
      else
        rx_data<=8'd0;//数据错误置零
    end
    else
      rx_data<=rx_data;
  end
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      rx_done<=1'b0;
    else
      rx_done<=rx_done_r;
  end
endmodule

module uart_tx #(
    parameter SYS_FRENCY = 50_000_000 ,//时钟
    parameter BAUD_FRENCY =115200//波特率
  )

  (
    input      clk       ,
    input      rst_n     ,
    input      send_en   ,   //发数据控制信号，脉冲触发
    input [7:0]tx_data   ,
    output reg rs232_tx  ,
    output     tx_done
  );
  localparam  CNT_MAX = SYS_FRENCY/BAUD_FRENCY;
  reg        en_cnt   ;
  reg [7:0]  r_tx_data;//用来寄存tx_data
  reg [3:0]  bps_cnt  ;
  reg [15:0] cnt      ;
  //r_tx_data
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      r_tx_data<='d0;
    else if(send_en)
      r_tx_data<=tx_data;
    else
      r_tx_data<=r_tx_data;
  end
  //en_cnt
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      en_cnt<=1'b0;
    else if(send_en)
      en_cnt<=1'b1;
    else if(cnt==(CNT_MAX>>1)+(CNT_MAX>>2)&&bps_cnt==8'd9)
      en_cnt<=1'b0;
    else
      en_cnt<=en_cnt;
  end
  //bps_cnt
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      bps_cnt<='d0;
    else if(en_cnt)
    begin
      if(cnt==CNT_MAX)
        bps_cnt<=bps_cnt+1'b1;
      else
        bps_cnt<=bps_cnt;
    end
    else
      bps_cnt<='d0;
  end
  //cnt
  always @(posedge clk or negedge rst_n)
  begin
    if(!rst_n)
      cnt<='d0;
    else if(en_cnt)
    begin
      if(cnt==CNT_MAX)
        cnt<='d0;
      else
        cnt<=cnt+1'b1;
    end
    else
      cnt<='d0;
  end

  //rs232_tx数据输出
  always @(*)
  begin
    if(!rst_n)
      rs232_tx<=1'b1;
    else if(en_cnt)
    begin
      case (bps_cnt)
        0:
          rs232_tx<=1'b0;
        1:
          rs232_tx<=r_tx_data[0];
        2:
          rs232_tx<=r_tx_data[1];
        3:
          rs232_tx<=r_tx_data[2];
        4:
          rs232_tx<=r_tx_data[3];
        5:
          rs232_tx<=r_tx_data[4];
        6:
          rs232_tx<=r_tx_data[5];
        7:
          rs232_tx<=r_tx_data[6];
        8:
          rs232_tx<=r_tx_data[7];
        9:
          rs232_tx<=1'b1;
        default:
          rs232_tx<=1'b1;
      endcase
    end
    else
      rs232_tx<=1'b1;
  end
  assign tx_done=(cnt==(CNT_MAX>>1)+(CNT_MAX>>2))&&bps_cnt==4'd9;
endmodule
